#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include "dfs_client.h"
#include "dfs_messages.h"
#include "dfs_multies.h"

/*===========================================================================*
 *				            connect_to_server                                *
 *===========================================================================*/
int connect_to_server(int port, char *ipaddr){
	
	int flg;
	int sd;
	
	struct sockaddr_in addr;
	

	memset(&addr,0,sizeof(addr));

	addr.sin_family  = AF_INET ;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ipaddr);

    /* Create TCP socket to establish connection to server */
	if((sd = socket(addr.sin_family,SOCK_STREAM,0))<0){
		printf("Socket create failure:%d\n",errno);
		perror("server ");
		exit(1);	
	}
	
	/* Connect to TCP Socket */
	if((flg=connect(sd, (struct sockaddr*)&addr, sizeof(addr)))<0){
			printf("Client connect failure:%d\n",errno);
			perror("Client: ");
			return NOTCONNECTED;
	}
	return sd;
}

/*===========================================================================*
 *				            get_client_request                               *
 *===========================================================================*/
char * get_client_request(char *request){
	/* Get client's requests */
	fgets(request, 2*CMDLEN, stdin);
    return request;
}

/*===========================================================================*
 *				            parse_client_shell                               *
 *===========================================================================*/
void parse_client_shell(){
	int sd,items,nbytes;
	dfs_message msg_in,msg_out;
	char *request,*cmd, *arg1,*arg2;
	char *connected_to,*presented;
	char buffer[BUFFER_SIZE];
	
	/* init of shel parser */
	request = malloc(REQUESTLEN*sizeof(char));
	cmd  = malloc(CMDLEN*sizeof(char));
    arg1 = malloc(CMDLEN*sizeof(char));
    arg2 = malloc(CMDLEN*sizeof(char));
	presented = malloc(CMDLEN*sizeof(char));
	memset(presented,0,CMDLEN);
	sprintf(presented,"@");
	connected_to = malloc(MAXIPADDRESSLEN*sizeof(char));
	memset(connected_to,0,MAXIPADDRESSLEN);
	sd   = NOTCONNECTED;
	items = 0;

    while(TRUE){ /* Shell's operations */
		memset(request,0,REQUESTLEN*sizeof(char));
		memset(cmd,0,CMDLEN*sizeof(char));
		memset(arg1,0,CMDLEN*sizeof(char));
		memset(arg2,0,CMDLEN*sizeof(char));
		
		printf("[Client%s]$ ",(sd==NOTCONNECTED)?" is not connected":presented);
        request = get_client_request(request);

        /* Parse the request and discretize to  *
         * type of request and alias            */
        items = sscanf(request, "%s %s %s", cmd, arg1 ,arg2);
        /* Handling client's requests */
		/* =================== DFS panel =================== */
        if(!strcmp(cmd,"open")){       /* Open */
			check_connection_and_params(sd,items,2); 
			if(make_open_msg(&msg_out,arg1,connected_to) != ERROR){
				message_to_string(&msg_out,buffer);
				write_to_fd_on_fail_exit(sd,buffer,"Write failed to server"); 
				read_from_fd_on_fail_exit(sd,buffer,"Read failed from server");
				string_to_message(&msg_in,buffer);
				if(handle_in_message(&msg_in) == OK){
					handle_opened_fd_list(arg1, OPEN);
					printf("[Opening file %s]\n",arg1);
				}else 
			        printf("[File not %s not found\n",arg1);
			}
        }
        else if(!strcmp(cmd,"read")){   /* Read */
			/* check if it is opened */
			check_connection_and_params(sd,items,2);
			printf("[Reading file %s]\n",arg1);
		}
        else if(!strcmp(cmd,"write")){  /* Write */
			/* check if its opened */
			check_connection_and_params(sd,items,2);
			printf("[Writing file %s]\n",arg1);
		}
        else if(!strcmp(cmd,"close")){  /* Close */
			check_connection_and_params(sd,items,2);
			printf("[Closing file %s]\n",arg1);
            handle_opened_fd_list(arg1, CLOSE);
		}
		/* =================== Administrative panel =================== */
        else if(!strcmp(cmd,"connect")){ /* Connect to a server */
			int port;
			if(items < 3){
				printf("[THIS COMMAND REQUIRES MORE ARGUMENTS]\n");
				continue;
			}
			if(sd != NOTCONNECTED){
				printf("[YOU ARE ALREADY CONNECTED TO A SERVER. PLEASE DISCONNECT FROM IT IN ORDER TO CONNECT TO ANOTHER]\n");
				continue;
			}
			items = sscanf(arg2,"%d",&port);
			if(items != 1){
				printf("[INVALID PORT NUMBER. PLEASE PROVIDE A VALID ONE]\n");
				continue;
			}
			sd=connect_to_server(port,arg1);
			if(sd == NOTCONNECTED){
				printf("[CANNOT CONNECT TO SERVER WITH IP ADDRESS : %s]\n",arg1);
				continue;
			}else{
				sprintf(connected_to,"%s",arg1);
				sprintf(presented,"@%s",connected_to);
				printf("[Connected!Server IP address :%s]\n",arg1);
			}
        }
		else if(!strcmp(cmd,"disconnect")){ /* Disconnect from server */
			if(sd == NOTCONNECTED){
				printf("[CLIENT IS NOT CONNECTED TO ANY SERVER]\n");
				continue;
			}
			close(sd);
			printf("[Disconnected from %s]\n",connected_to);
			memset(connected_to,0,MAXIPADDRESSLEN);
			memset(presented,0,CMDLEN);
			sprintf(presented,"@");
			sd = NOTCONNECTED;
        }
        else if(!strcmp(cmd,"connect_p2p")){ /* Force server to connect to one of it's peers */
			check_connection_and_params(sd,items,3);	
			if(make_connect_p2p_msg(&msg_out,arg1,arg2,connected_to) != ERROR){
				message_to_string(&msg_out,buffer);
				write_to_fd_on_fail_exit(sd,buffer,"Write failed to server"); 
				read_from_fd_on_fail_exit(sd,buffer,"Read failed from server");
				string_to_message(&msg_in,buffer);
				if(handle_in_message(&msg_in) == OK)
					printf("[Connecting peers ( %s -> %s:%s )]\n",connected_to,arg1,arg2);
				else
					printf("[SERVER %s COULD NOT CONNECT with %s]\n",connected_to,arg1);
			}else
				printf("[ERROR OCCURED WHILE BUILDING MESSAGE (%s)]\n",cmd);
        }
		else if(!strcmp(cmd,"disconnect_p2p")){ /* Disconnect from server */
			check_connection_and_params(sd,items,2);
			printf("[Disconnecting peers (%s,%s)]\n",connected_to,arg1);
        }
        else if(!strcmp(cmd,"update_cache")){ /* update cache of the connected server manually */
			check_connection(sd);
			printf("[Cache is now up-to-date for server %s]\n",connected_to);
        }
        else if(!strcmp(cmd,"clear_cache")){ /* clear cache of the server connected */
			check_connection(sd);
			printf("[Cache is now cleared for server %s]\n",connected_to);
        }
        else if(!strcmp(cmd,"get_log")){ /* Get logs from server */
			check_connection(sd);
			printf("[Client received logs from server %s]\n",connected_to);
        }
		else if(!strcmp(cmd,"get_neighbours")){ /* Get server's neighbours */
			check_connection(sd);		
			printf("[Client received server %s neighbours]\n",connected_to);
        }
        else if(!strcmp(cmd,"remove_file")){ 
			check_connection_and_params(sd,items,2);
			if (check_if_opened(arg1) == OK){
				printf("[Cannot remove_file because it is opened]\n");
			}else{
				if(make_rmfile_msg(&msg_out,arg1,connected_to) != ERROR){
					message_to_string(&msg_out,buffer);
					write_to_fd_on_fail_exit(sd,buffer,"Write failed to server");
					printf("[Client sent server %s a removing request for file %s ]\n",connected_to,arg1);
					read_from_fd_on_fail_exit(sd,buffer,"Read failed from server");
					string_to_message(&msg_in,buffer);
					if(handle_in_message(&msg_in) == OK)
						printf("[Removing request of %s from %s is approved ]\n",arg1,connected_to);
					else
						printf("[Removing request of %s from %s is REJECTED !]\n",arg1,connected_to);
				}else
					printf("[ERROR OCCURED WHILE BUILDING MESSAGE (%s)]\n",cmd);
			}
        }
 		else if(!strcmp(cmd,"share_file")){ /* Force server to share a local file */
			check_connection_and_params(sd,items,2);
			if(make_sharing_msg(&msg_out,arg1,connected_to) != ERROR){
				message_to_string(&msg_out,buffer);
				write_to_fd_on_fail_exit(sd,buffer,"Write failed to server");
				printf("[Client sent server %s a sharing request for file %s ]\n",connected_to,arg1);
				read_from_fd_on_fail_exit(sd,buffer,"Read failed from server");
				string_to_message(&msg_in,buffer);
				if(handle_in_message(&msg_in) == OK)
					printf("[Sharing request of %s from %s is approved ]\n",arg1,connected_to);
				else
					printf("[Sharing request of %s from %s is REJECTED !]\n",arg1,connected_to);
			}else
				printf("[ERROR OCCURED WHILE BUILDING MESSAGE (%s)]\n",cmd);
        }
        else if(!strcmp(cmd,"get_files")){ /* Get server's files */
			check_connection(sd);
			if(make_get_files_msg(&msg_out,connected_to) != ERROR){
				message_to_string(&msg_out,buffer);
				write_to_fd_on_fail_exit(sd,buffer,"Write failed to server");
				printf("[Client sent server %s a sharing request for file %s ]\n",connected_to,arg1);
				read_from_fd_on_fail_exit(sd,buffer,"Read failed from server");
				string_to_message(&msg_in,buffer);
				if(handle_in_message(&msg_in) == OK)
					printf("\n");
				else
					printf("[Server failed returning files]\n",arg1,connected_to);
			}else
				printf("[ERROR OCCURED WHILE BUILDING MESSAGE (%s)]\n",cmd);
			printf("[Client received server %s files]\n",connected_to);
        }
		else if(!strcmp(cmd,"help")){  /* help  */
			splasher();
        }
        else if(!strcmp(cmd,"exit")){  /* Exit  */
			if( sd != NOTCONNECTED) close(sd);
			printf("Exiting...");
			break;
        }else{
            printf("[%s : Command not found]\n",cmd); 
            continue;
        }
    }
}

/*===========================================================================*
 *				            splasher                                         *
 *===========================================================================*/
void splasher(){
    
    printf("\n");
    printf("/*===========================================================*\n");
    printf(" * Distributed File System implemented for academic purposes *\n");
    printf(" * for the CEID's course Operating Systems II	during 2009  *\n");
    printf(" * Authors: John Gakos                                       *\n");
    printf(" *          Dimitris Mpousis                                 *\n");
    printf(" *===========================================================*/\n");
    printf("\n");
    printf(" Client's User Interface\n");
    printf(" =======================\n");
    printf(" {*} Open <filename>\n");
    printf(" {*} Read <filename>\n");
    printf(" {*} Write <filename>\n");
    printf(" {*} Close <filename>\n");
    printf("\n");
    printf(" -------------------------\n");
    printf(" -------------------------\n");
    printf("\n");
    printf(" Adminstrative Panel\n");
    printf(" =======================\n");
    printf(" {*} Connect to server [connect <ip address> <port>]\n");
	printf(" {*} Disconnect from server [disconnect]\n");
    printf(" {*} Connect to peer [connect_p2p <ip address> <port>]\n");
	printf(" {*} Disconnect a peer [disconnect_p2p <ip address>]\n");
    printf(" {*} Update Cache [update_cache]\n");
	printf(" {*} Clear Cache [clear_cache]\n");
    printf(" {*} Log [get_log]\n");
	printf(" {*} Get neighbours [get_neighbours]\n");
	printf(" {*} Share a file [share_file <filename>]\n");
	printf(" {*} Remove a shared file [remove_file <filename>]\n");
	printf(" {*} Get shared files [get_files]\n");
	printf(" {*} Help [help]\n");
	printf(" {*} Exit [exit]\n");
	printf("========\n");
}
/*===========================================================================*
 *				            get_my_IP_address                                *
 *===========================================================================*/
const char *get_my_IP_address(){
	char hostname[BUFFER_SIZE];
	int i;
	
	struct hostent *he;
	/* find my IP address */
	gethostname(hostname,BUFFER_SIZE);
	he = gethostbyname(hostname);
	return inet_ntoa(*(struct in_addr *)he->h_addr_list[1]);
}

int main(int argc, char **argv)
{   
    int sd, udpsd;
    char *ipaddr;

	splasher();
	/*=========================*/
	/* Retrieve IP and set the PROPER ONE : 192.168.54.1 */
    memset(my_ip_address,0,sizeof(my_ip_address));
	sprintf(my_ip_address,"%s",get_my_IP_address());
	if(!strcmp(my_ip_address,"192.168.54.1"))
		printf("[Your IP address is %s]\n",my_ip_address);
	else{
		printf("[Your IP address is 192.168.54.1]\n");
		sprintf(my_ip_address,"192.168.54.1");
	} 
	/*=========================*/
	/* Init opened files' list */
    the_list = NULL;
    /*=========================*/
	parse_client_shell();
	printf("Bye\n");
    return 0;
}